import os
import asyncio
import re
import json
import uuid
import shutil
import httpx
import aiofiles
from fake_useragent import UserAgent
from fastapi import HTTPException
from bs4 import BeautifulSoup
ua = UserAgent(browsers=['chrome','edge'])


async def Get_B_video(url: str):
    headers = {
    'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
    'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
    'priority': 'u=0, i',
    'referer': 'https://www.bilibili.com/',
    'sec-ch-ua': '"Chromium";v="136", "Microsoft Edge";v="136", "Not.A/Brand";v="99"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Windows"',
    'sec-fetch-dest': 'document',
    'sec-fetch-mode': 'navigate',
    'sec-fetch-site': 'same-origin',
    'sec-fetch-user': '?1',
    'upgrade-insecure-requests': '1',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0',
    'cookie': "buvid3=5CF86B37-4342-79FE-9B44-8513AE832AFB66134infoc; b_nut=1747276366; _uuid=4A7E5957-910C5-EDF9-DCE2-CF86491AA108C69795infoc; buvid_fp=f9138edbce438b1c060fa872244e0ac0; header_theme_version=CLOSE; enable_web_push=DISABLE; enable_feed_channel=ENABLE; home_feed_column=5; buvid4=4816D266-2EBC-D20C-94A6-429C463946EB66985-025051510-LkqPWO+r28LRUXjImpKzEA%3D%3D; rpdid=|(k|)Rl~l|Ru0J'u~R~k~YJ)R; DedeUserID=1633105049; DedeUserID__ckMd5=ca892d695f79cf32; CURRENT_QUALITY=80; hit-dyn-v2=1; theme-tip-show=SHOWED; theme-avatar-tip-show=SHOWED; bp_t_offset_1633105049=1105712461039796224; b_lsid=4FC3F5EC_19913253D28; bmg_af_switch=1; bmg_src_def_domain=i0.hdslb.com; bili_ticket=eyJhbGciOiJIUzI1NiIsImtpZCI6InMwMyIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NTcyMjIwMzIsImlhdCI6MTc1Njk2Mjc3MiwicGx0IjotMX0.9PBq8-F6cxoJ1PC1TIJnzW_TvlOozohtRJ8DEDvGNB4; bili_ticket_expires=1757221972; SESSDATA=34a3df17%2C1772514832%2C57b25%2A91CjASGNcta0fIzHKWGe6DDCDdaZ7dnk485WMQRxBdxD4kpWitBkmg9FKZtT14BxGGkNASVnhPRnEwMm1PXzZmT1JTV085bGRkQWRSSy1VX0RPQ1BxZllPX2xCMENOZVZtaWI1OUhKUEttY2pacVFVVjdGZm9yaHFycDNsX1lmVEJXNjVlRFRzV3lBIIEC; bili_jct=d2be96272e1a8cd43628f44361b45c51; sid=6f3vq7hl; browser_resolution=1912-431; CURRENT_FNVAL=4048",
    }
    async with httpx.AsyncClient(headers=headers, timeout=30.0) as client:
        try:
            # 1. 获取页面 HTML
            res = await client.get(url)
            html = res.text
            soup = BeautifulSoup(html, 'html.parser')
            # 提取视频信息
            try:
                json_str = re.findall('window.__playinfo__=(.*?)</script>', html, re.S)[0]
                json_data = json.loads(json_str)
            except Exception as e:
                raise HTTPException(status_code=400, detail="未找到视频信息，请检查链接是否正确")

            # 提取标题
            title = soup.title.text
            if not title:
                raise HTTPException(status_code=400, detail="未找到视频标题")
            # 清理标题中的特殊字符
            title = re.sub(r'[\\/*?:"<>|]', "", title)

            # 生成uuid（相对路径用于返回，绝对路径用于保存）
            file_id = str(uuid.uuid4())[:8]  # 短UUID
            cover_path = f'static/b_videos/cover_{file_id}.png'
            audio_path = f'static/b_videos/audio_{file_id}.mp3'
            video_path = f'static/b_videos/video_{file_id}.mp4'
            full_video_path = f'static/b_videos/full_video_{file_id}.mp4'

            # 确保目录存在
            os.makedirs(os.path.dirname(cover_path), exist_ok=True)

            # 2. 下载封面
            cover_url_match = re.findall('<meta data-vue-meta="true" itemprop="image" content="(.*?)">', html)
            if not cover_url_match:
                raise HTTPException(status_code=400, detail="未找到封面")
            cover_url = cover_url_match[0]
            if not cover_url.startswith('http'):
                cover_url = "https:" + cover_url

            cover_response = await client.get(cover_url)
            async with aiofiles.open(cover_path, 'wb') as f:
                await f.write(cover_response.content)
            print('封面保存完成！')

            # 3. 下载音频
            try:
                audio_url = json_data['data']['dash']['audio'][0]['baseUrl']
                # 添加必要的 headers
                audio_headers = headers.copy()
                audio_headers.update({
                    'Range': 'bytes=0-',
                    'Origin': 'https://www.bilibili.com'
                })
                audio_response = await client.get(audio_url, headers=audio_headers)
                async with aiofiles.open(audio_path, 'wb') as f:
                    await f.write(audio_response.content)
                print('音频保存完成！')
            except Exception as e:
                # 清理已下载的封面
                if os.path.exists(cover_path):
                    os.remove(cover_path)
                raise HTTPException(status_code=500, detail=f"音频下载失败: {str(e)}")

            # 4. 下载视频
            try:
                video_url = json_data['data']['dash']['video'][0]['baseUrl']
                # 添加必要的 headers
                video_headers = headers.copy()
                video_headers.update({
                    'Range': 'bytes=0-',
                    'Origin': 'https://www.bilibili.com'
                })
                video_response = await client.get(video_url, headers=video_headers)
                async with aiofiles.open(video_path, 'wb') as f:
                    await f.write(video_response.content)
                print('视频保存完成！')
            except Exception as e:
                # 清理已下载的文件
                for f in [cover_path, audio_path]:
                    if os.path.exists(f):
                        os.remove(f)
                raise HTTPException(status_code=500, detail=f"视频下载失败: {str(e)}")

            # 5. 合并音视频（异步执行 ffmpeg）
            try:
                print('开始合并音视频...')
                proc = await asyncio.create_subprocess_exec(
                    'ffmpeg', '-i', audio_path, '-i', video_path,
                    '-c:v', 'copy', '-c:a', 'copy', '-bsf:a', 'aac_adtstoasc', full_video_path,
                    stdout=asyncio.subprocess.PIPE,
                    stderr=asyncio.subprocess.PIPE
                )
                stdout, stderr = await proc.communicate()

                if proc.returncode != 0:
                    raise HTTPException(status_code=500, detail=f"合并失败: {stderr.decode()}")

                print('合并成功')
            except Exception as e:
                # 清理已下载的文件
                for f in [cover_path, audio_path, video_path, full_video_path]:
                    if os.path.exists(f):
                        os.remove(f)
                raise HTTPException(status_code=500, detail=f"音视频合并失败: {str(e)}")
            finally:
                # 清理临时文件
                if os.path.exists(video_path):
                    os.remove(video_path)

            # 返回结果
            return title, f"/b_videos/full_video_{file_id}.mp4", file_id

        except httpx.RequestError as e:
            raise HTTPException(status_code=500, detail=f"网络请求失败: {str(e)}")
        except HTTPException:
            # 重新抛出已定义的HTTP异常
            raise
        except Exception as e:
            raise HTTPException(status_code=500, detail=f"下载失败: {str(e)}")
